package org.faster.dal.core;

import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.faster.dal.cache.CacheManager;
import org.faster.dal.criteria.Model;
import org.faster.dal.criteria.QueryCriteria;
import org.faster.dal.descriptor.QueryResult;
import org.faster.dal.descriptor.Table;
import org.faster.dal.descriptor.db.ResolveDataBase;
import org.faster.dal.internal.util.message.Messages;
import org.faster.dal.logging.Log;
import org.faster.dal.logging.LogFactory;
import org.faster.dal.router.MasterSlaveRouter;

public abstract class AbstractDAL implements BaseDAL {

 private static final Log LOG = LogFactory.getLog(AbstractDAL.class);
    
    private CacheManager cacheManager;
    
    private ResolveDataBase resolveDatabase;
    
    private MasterSlaveRouter router;

    @Override
    public QueryResult selectByCriteria(List<String> fields, QueryCriteria queryCriteria) {
        if(router != null){
            router.routeToSlave();
        }
        
        QueryResult queryResult = new QueryResult();
        
        int hashcode = 0;
        if(fields != null){
            for(String str : fields){
                hashcode += str.hashCode();
            }
        }
        hashcode += queryCriteria.hashCode();
        String cacheKey = queryCriteria.getTable()+"_selectByCriteria_" + hashcode;
        if(cacheManager != null){
            List<Map<String, Object>> value = (List<Map<String, Object>>) cacheManager.getCache().getObject(cacheKey);
            if(value != null){
                queryResult.setResultList(value);
                return queryResult;
            }
        }
        Table table = retrievalTableByQueryCriteria(queryCriteria);
        if(fields != null && fields.size() > 0){
            LinkedHashMap<String, Object> fieldMap = new LinkedHashMap<String, Object>();
            for(String field : fields){
                if(table.getFields().containsKey(field)){
                    fieldMap.put(field, true);
                }
            }
            table.setParams(fieldMap);
        }
        table.setQueryCriteria(queryCriteria);
        
        List<Map<String, Object>> result = selectByCriteria(table);
        
        if(cacheManager != null){
            cacheManager.getCache().putObject(cacheKey, result);
        }
        
        if(result != null){
            queryResult.setResultList(result);
            return queryResult;
        }else{
            return null;
        }
    }
    
    public abstract List<Map<String, Object>> selectByCriteria(Table table);
    public abstract int countByCriteria(Table table);
    public abstract Map<String, Object> selectByPrimaryKey(Table table);
    public abstract int insert(Table table);
    public abstract int updateByCriteria(Table table);
    public abstract int updateByPrimaryKey(Table table);
    public abstract int deleteByPrimaryKey(Table table);
    public abstract int deleteByCriteria(Table table);

    /**
     * 获取表的bo对象
     * @param queryCriteria
     * @return
     */
    private Table retrievalTableByQueryCriteria(QueryCriteria queryCriteria) {
        if(queryCriteria == null || StringUtils.isEmpty(queryCriteria.getTable())){
            LOG.error(Messages.getString("RuntimeError.8", "queryCriteria"));
            throw new RuntimeException(Messages.getString("RuntimeError.8", "queryCriteria"));
        }
        Table table = (Table) cacheManager.getCache().getObject(queryCriteria.getTable());
        if(table == null){
            table = resolveDatabase.loadTable(queryCriteria.getTable());
            if(table == null){
                LOG.error(Messages.getString("RuntimeError.9", queryCriteria.getTable()));
                throw new RuntimeException(Messages.getString("RuntimeError.9", queryCriteria.getTable()));
            }
        }
        return table;
    }

    @Override
    public int countByCriteria(QueryCriteria queryCriteria) {
        if(router != null){
            router.routeToSlave();
        }
        int hashcode = 0;
        hashcode += queryCriteria.hashCode();
        String cacheKey = queryCriteria.getTable()+"_countByCriteria_" + hashcode;
        if(cacheManager != null){
            Integer value = (Integer) cacheManager.getCache().getObject(cacheKey);
            if(value != null){
                return value;
            }
        }
        Table table = retrievalTableByQueryCriteria(queryCriteria);
        table.setQueryCriteria(queryCriteria);
        int result = countByCriteria(table);
        if(cacheManager != null){
            cacheManager.getCache().putObject(cacheKey, result);
        }
        return result;
    }
    
    @Override
    public QueryResult selectByPrimaryKey(Model model) {
        if(router != null){
            router.routeToSlave();
        }
        QueryResult queryResult = new QueryResult();
        int hashcode = 0;
        hashcode += model.hashCode();
        String cacheKey = model.getTableName()+"_selectByPrimaryKey_" + hashcode;
        if(cacheManager != null){
            Map<String, Object> value = (Map<String, Object>) cacheManager.getCache().getObject(cacheKey);
            if(value != null){
                queryResult.setResultMap(value);
                return queryResult;
            }
        }
        Table table = retrievalTableByModel(model);
        if(model != null && model.getContent() != null && model.getContent().size() > 0){
            List<String> names = table.getPrimaryKey().getFields();
            if(model.getContent().containsKey(Model.MODEL_ID)){
                LinkedHashMap<String, Object> condistions = new LinkedHashMap<String, Object>();
                condistions.put(names.get(0), model.getContent().get(Model.MODEL_ID));
                table.setConditions(condistions);
            }else{
                table.setConditions(model.getContent());
            }
        }else{
            LOG.error(Messages.getString("RuntimeError.8", "model.conditions"));
            throw new RuntimeException(Messages.getString("RuntimeError.8", "model.conditions"));
        }
        Map<String, Object> result = selectByPrimaryKey(table);
        
        if(cacheManager != null){
            cacheManager.getCache().putObject(cacheKey, result);
        }
        if(result != null){
            queryResult.setResultMap(result);
            return queryResult;
        }else{
            return null;
        }
    }
    
    
    
    /**
     * 获取表的bo对象
     * @param queryCriteria
     * @return
     */
    private Table retrievalTableByModel(Model model) {
        if(model == null || StringUtils.isEmpty(model.getTableName())){
            LOG.error(Messages.getString("RuntimeError.8", "model"));
            throw new RuntimeException(Messages.getString("RuntimeError.8", "model"));
        }
        Table table = (Table) cacheManager.getCache().getObject(model.getTableName());
        if(table == null){
            table = resolveDatabase.loadTable(model.getTableName());
            if(table == null){
                LOG.error(Messages.getString("RuntimeError.9", model.getTableName()));
                throw new RuntimeException(Messages.getString("RuntimeError.9", model.getTableName()));
            }
        }
        return table;
    }


    @Override
    public int insert(Model model) {
        if(router != null){
            router.routeToMaster();
        }
        Table table = retrievalTableByModel(model);
        if(model != null && model.getContent() != null && model.getContent().size() > 0){
            table.setParams(model.getContent());
        }else{
            LOG.error(Messages.getString("RuntimeError.8", "model.params"));
            throw new RuntimeException(Messages.getString("RuntimeError.8", "model.params"));
        }
        
        int result = insert(table);
        
        if(cacheManager != null){
            cacheManager.getCache().clear(firstToUpper(model.getTableName()));
        }
        
        return result;
    }
    
    

    @Override
    public int updateByCriteria(Model model, QueryCriteria queryCriteria) {
        if(router != null){
            router.routeToMaster();
        }
        Table table = retrievalTableByQueryCriteria(queryCriteria);
        if(model != null && model.getContent() != null && model.getContent().size() > 0){
            table.setParams(model.getContent());
        }else{
            LOG.error(Messages.getString("RuntimeError.8", "model.params"));
            throw new RuntimeException(Messages.getString("RuntimeError.8", "model.params"));
        }
        
        int result = updateByCriteria(table);
        
        if(cacheManager != null){
            cacheManager.getCache().clear(firstToUpper(model.getTableName()));
        }
        
        return result;
    }
    
    

    @Override
    public int updateByPrimaryKey(Model model) {
        if(router != null){
            router.routeToMaster();
        }
        Table table = retrievalTableByModel(model);
        if(model != null && model.getContent() != null && model.getContent().size() > 0){
            List<String> names = table.getPrimaryKey().getFields();
            if(model.getContent().containsKey(Model.MODEL_ID) && names.size() == 1){
                model.getContent().put(names.get(0), model.getContent().get(Model.MODEL_ID));
            }
            LinkedHashMap<String, Object> params = new LinkedHashMap<String, Object>();
            LinkedHashMap<String, Object> conditions = new LinkedHashMap<String, Object>();
            Iterator<String> iter = model.getContent().keySet().iterator();
            while(iter.hasNext()){
                String key = iter.next();
                if(table.getPrimaryKey().getFields().contains(key)){
                    conditions.put(key, params.get(key));
                }else{
                    params.put(key, params.get(key));
                }
            }
            table.setParams(params);
            table.setConditions(conditions);
        }else{
            LOG.error(Messages.getString("RuntimeError.8", "model.params"));
            throw new RuntimeException(Messages.getString("RuntimeError.8", "model.params"));
        }
        int result = updateByPrimaryKey(table);
        
        if(cacheManager != null){
            cacheManager.getCache().clear(firstToUpper(model.getTableName()));
        }
        
        return result;
    }
    
    

    @Override
    public int deleteByPrimaryKey(Model model) {
        if(router != null){
            router.routeToMaster();
        }
        Table table = retrievalTableByModel(model);
        if(model != null && model.getContent() != null && model.getContent().size() > 0){
            List<String> names = table.getPrimaryKey().getFields();
            if(model.getContent().containsKey(Model.MODEL_ID) && names.size() == 1){
                LinkedHashMap<String, Object> condistions = new LinkedHashMap<String, Object>();
                condistions.put(names.get(0), model.getContent().get(Model.MODEL_ID));
                table.setConditions(condistions);
            }else{
                table.setConditions(model.getContent());
            }
        }else{
            LOG.error(Messages.getString("RuntimeError.8", "model.conditions"));
            throw new RuntimeException(Messages.getString("RuntimeError.8", "model.conditions"));
        }
        
        int result = deleteByPrimaryKey(table);
        if(cacheManager != null){
            cacheManager.getCache().clear(firstToUpper(model.getTableName()));
        }
        
        return result;
    }
    
    

    @Override
    public int deleteByCriteria(QueryCriteria queryCriteria) {
        if(router != null){
            router.routeToMaster();
        }
        Table table = retrievalTableByQueryCriteria(queryCriteria);
        int result = deleteByCriteria(table);
        if(cacheManager != null){
            cacheManager.getCache().clear(firstToUpper(queryCriteria.getTable()));
        }
        return result;
    }
    
    public void reloadTable(String tableName){
        this.resolveDatabase.reloadTable(tableName);
    }
    
    
    @Override
    public QueryResult selectByCriteria(QueryCriteria queryCriteria) {
        return selectByCriteria(null, queryCriteria);
    }

    public void setCacheManager(CacheManager cacheManager) {
        this.cacheManager = cacheManager;
    }

    public void setResolveDatabase(ResolveDataBase resolveDatabase) {
        this.resolveDatabase = resolveDatabase;
    }
    
    public static String firstToUpper(String str){
        char[] array = str.toCharArray();
        array[0] -= 32;
        return String.valueOf(array);
    }

}
